/**
 * 
 */
package com.alphasystem.commerce.common.util;

import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
import static javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.SAXException;

/**
 * @author Syed Farhan Ali
 * 
 */
public class JAXBUtil {

	public static <T> void marshall(File file, String contextPath,
			JAXBElement<T> jaxbElement, String... schemaPath) {
		BufferedWriter writer = null;
		try {
			writer = new BufferedWriter(new FileWriter(file));
			marshall(writer, contextPath, jaxbElement, schemaPath);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
				}
			}
		}
	}

	public static <T> void marshall(OutputStream stream, String contextPath,
			JAXBElement<T> jaxbElement, String... schemaPath) {
		OutputStreamWriter writer = null;
		try {
			writer = new OutputStreamWriter(stream);
			marshall(writer, contextPath, jaxbElement, schemaPath);
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			if (writer != null) {
				try {
					writer.close();
				} catch (IOException e) {
				}
			}
		}
	}

	public static <T> String marshall(String contextPath,
			JAXBElement<T> jaxbElement, String... schemaPath) {
		String result = null;
		StringWriter writer = new StringWriter();
		try {
			marshall(writer, contextPath, jaxbElement, schemaPath);
			result = writer.toString();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		} finally {
			try {
				writer.close();
			} catch (IOException e) {
			}
		}
		return result;
	}

	public static <T> void marshall(Writer writer, String contextPath,
			JAXBElement<T> jaxbElement, String... schemaPath)
			throws JAXBException, SAXException {
		JAXBContext jaxbContext = JAXBContext.newInstance(contextPath);
		Marshaller marshaller = jaxbContext.createMarshaller();
		if (schemaPath != null && schemaPath.length > 0) {
			SchemaFactory schemaFactory = SchemaFactory
					.newInstance(W3C_XML_SCHEMA_NS_URI);
			Source[] schemas = new StreamSource[schemaPath.length];
			for (int i = 0; i < schemaPath.length; i++) {
				schemas[i] = new StreamSource(
						AppUtil.getResourceAsStream(schemaPath[i]));
			}
			Schema schema = schemaFactory.newSchema(schemas);
			marshaller.setSchema(schema);
		}
		marshaller.setProperty(JAXB_FORMATTED_OUTPUT, true);
		marshaller.marshal(jaxbElement, writer);
	}

	@SuppressWarnings("unchecked")
	public static <T> T unmarshal(Class<T> klass, InputStream source,
			String... schemaPath) {
		T result = null;
		try {
			JAXBContext jaxbContext = JAXBContext.newInstance(klass
					.getPackage().getName());
			Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
			if (schemaPath != null && schemaPath.length > 0) {
				SchemaFactory schemaFactory = SchemaFactory
						.newInstance(W3C_XML_SCHEMA_NS_URI);
				Source[] schemas = new StreamSource[schemaPath.length];
				for (int i = 0; i < schemaPath.length; i++) {
					schemas[i] = new StreamSource(
							AppUtil.getResourceAsStream(schemaPath[i]));
				}
				Schema schema = schemaFactory.newSchema(schemas);
				unmarshaller.setSchema(schema);
			}
			JAXBElement<T> t = (JAXBElement<T>) unmarshaller.unmarshal(source);
			result = t.getValue();
		} catch (Exception e) {
			throw new RuntimeException(e.getMessage(), e);
		}
		return result;
	}
}
